/**
 * TabVD is a Virtual Desktop Client/Server designed specifically for connecting
 * from Tablet like devices.
 *
 * Copyright (C) 2011 Subhabrata Ghosh
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * FileLogger.cpp
 *
 *  Created on: May 27, 2011
 *      Author: subhagho
 */

#include "FileLogger.h"

#include <boost/algorithm/string.hpp>

namespace tabvd {

    FileLogger::FileLogger() {
        directory = NULL;
        filename = NULL;
        stream = NULL;
        config = NULL;

        dobackup = false;
        maxsize = -1;

    }

    FileLogger::~FileLogger() {
        if (filename)
            delete filename;
        closestream();
    }

    void FileLogger::init(AbstractConfiguration* config){
        try {
            if(!update(config))
                throw Exception("Error initializing FileLogger, invalid configuration...");

            checksize(false);

            if (!openstream())
                throw Exception("Error initializing FileLogger, file stream creation failed...");

            DateTime dt;
            print("FileLogger Initialized : %s", dt.toString()->c_str());
            print("FileLogger Log Level : %d", loglevel);

            isrunning = true;

        } catch (Exception& ex) {
            printf("Error initializing FileLogger : %s\n", ex.what());
            throw ex;
        } catch (...) {
            throw Exception("Error initializing FileLogger...");
        }
    }

    void FileLogger::log(ELevel level, const char* format, ...){
        if (!shouldlog(level))
            return;
        try {
            va_list args;
            va_start(args, format);
            std::string* mesg = getmessage(level, format, args);
            va_end(args);
            if (mesg) {
                if (stream->good()){
                    stream->write(mesg->c_str(), mesg->length());
                } else {
                    isrunning = false;
                }
                delete mesg;
            }
        } catch (...) {
            isrunning = false;
            printf("FileLogger : ERROR : Failed to write to file, logger shutting down...\n");
        }
    }

    void FileLogger::print(const char* format, ...){
        try {
            va_list args;
            va_start(args, format);
            std::string* mesg = getmessage(LOGLEVEL_PRINT, format, args);
            va_end(args);
            if (mesg) {
                if (stream->good()){
                    stream->write(mesg->c_str(), mesg->length());
                } else {
                    isrunning = false;
                }
                delete mesg;
            }
        } catch (...) {
            isrunning = false;
            printf("FileLogger : ERROR : Failed to write to file, logger shutting down...\n");
        }
    }

    void FileLogger::clear() {
        bool odbck = dobackup;

        dobackup = false;
        checksize(true);
        dobackup = odbck;
    }

    void FileLogger::backup() {
        checksize(true);
    }

    bool FileLogger::update(AbstractConfiguration* config){
        if (!config->containsKey(FileLogger::_CONFIG_KEY_DIRECTORY_))
            throw Exception("Cannot find configuration parameter [directory].");
        std::string dir = config->get(FileLogger::_CONFIG_KEY_DIRECTORY_);
        if (directory) {
            if (directory->compare(dir) != 0){
                directory->assign(dir);
            }
        } else {
            directory = new std::string(dir);
        }
        if (!config->containsKey(FileLogger::_CONFIG_KEY_FILENAME_))
            throw Exception("Cannot find configuration parameter [filename].");
        std::string file = config->get(FileLogger::_CONFIG_KEY_FILENAME_);
        if (filename) {
            if (filename->compare(file) != 0) {
                filename->assign(file);
            }
        } else {
            filename = new std::string(file);
        }
        if (config->containsKey(FileLogger::_CONFIG_KEY_LEVEL_)) {
            std::string slevel = config->get(FileLogger::_CONFIG_KEY_LEVEL_);
            if (!slevel.empty()){
                if (slevel.compare("ERROR") == 0 || slevel.compare("error") == 0)
                    loglevel = LOGLEVEL_ERROR;
                else if (slevel.compare("WARNING") == 0 || slevel.compare("warning") == 0)
                    loglevel = LOGLEVEL_WARNING;
                else if (slevel.compare("DEBUG") == 0 || slevel.compare("debug") == 0)
                    loglevel = LOGLEVEL_DEBUG;
            }
        }
        if (config->containsKey(FileLogger::_CONFIG_KEY_MAXSIZE_)) {
            std::string smax = config->get(FileLogger::_CONFIG_KEY_MAXSIZE_);
            if (!smax.empty()){
                boost::trim(smax);                
                int factor = 1;
                char cc = smax.at(smax.length() - 1);
                if (cc == 'm' || cc == 'M')
                    factor *= (1024 * 1024);
                else if (cc == 'k' || cc == 'K')
                    factor *= 1024;

                if (!isdigit(cc))
                    smax = smax.substr(0, smax.length() - 1);
                int size = atoi(smax.c_str());
                maxsize = size * factor;
            }
        }
        if (config->containsKey(FileLogger::_CONFIG_KEY_BACKUP_)) {
            std::string bck = config->get(FileLogger::_CONFIG_KEY_BACKUP_);
            if (!bck.empty()){
                if (bck.compare("true") == 0 || bck.compare("TRUE") == 0){
                    dobackup = true;
                }
            }
        }
        this->config = config;

        return true;
    }

    bool FileLogger::createDefault(AbstractConfiguration* nconfig) {
        /*
           static const std::string _CONFIG_KEY_DIRECTORY_;
           static const std::string _CONFIG_KEY_FILENAME_;
           static const std::string _CONFIG_KEY_LEVEL_;
           static const std::string _CONFIG_KEY_MAXSIZE_;
           static const std::string _CONFIG_KEY_BACKUP_;
         */

        // Default log directory = $TEMP/log/
        std::string tmpdir = getenv("TEMP");
        tmpdir.append("/log/");
        nconfig->set(FileLogger::_CONFIG_KEY_DIRECTORY_, tmpdir);

        // Default log filename = logger.log
        nconfig->set(FileLogger::_CONFIG_KEY_FILENAME_, std::string("logger.log"));

        // Default logging level = ERROR
        nconfig->set(FileLogger::_CONFIG_KEY_LEVEL_, std::string("ERROR"));

        // Default Max filesize = -1 
        nconfig->set(FileLogger::_CONFIG_KEY_MAXSIZE_, std::string("-1"));

        // Default Backup log = false
        nconfig->set(FileLogger::_CONFIG_KEY_BACKUP_, std::string("false"));
    }

    bool FileLogger::checksize(bool force) {
        bool streamclosed = closestream();

        std::string path = getfilename();

        if (!force) {
            if (maxsize <= 0)
                return true;

            int sizef = FileUtils::sizef(path);
            if (sizef < 0)
                return false;

            if (sizef < maxsize)
                return true;
        }
        if (dobackup) {
            DateTime dt;
            std::string bkname(*directory);
            bkname.append("/").append(*dt.toString("%Y%m%d%H%M"));
            bkname.append("_").append(*filename);

            if(!FileUtils::rename(path, bkname))
                return false;
        } else {
            if(!FileUtils::remove(path))
                return false;
        }
        if (streamclosed)
            openstream();

        return true;
    }

    std::string FileLogger::getfilename() {
        std::string path(*directory);
        path.append("/").append(*filename);
        return path;
    }

    bool FileLogger::openstream() {
        closestream();

        std::string path = getfilename();
        stream = new std::ofstream(path.c_str(), std::fstream::app);
        if (!stream)
            return false;
        return true;
    }

    bool FileLogger::closestream() {
        if (stream) {
            if (stream->good()){
                stream->flush();
                stream->close();
            }
            delete stream;
            stream = NULL;
            return true;
        }
        return false;
    }
}
